from typing import Optional

from sunverse.objects.attachments import Photo, Snippet, Video


class Postlike:
    """Represents a Weverse Post-Like Object.

    Post-Like Objects refer to the different types of Weverse contents that
    share a similar data structure with each other. Examples of which are posts
    that fall under the categories of `Post`, `Moment`, `Media` and `Live`.

    Inherited by:

    - :class:`sunverse.objects.Post`
    - :class:`sunverse.objects.Medialike`
    - :class:`sunverse.objects.ImageMedia`
    - :class:`sunverse.objects.YoutubeMedia`
    - :class:`sunverse.objects.WeverseMedia`
    - :class:`sunverse.objects.Live`

    .. container:: operations

        .. describe:: x == y

            Checks if two post-like objects are equal.

        .. describe:: x != y

            Checks if two post-like objects are not equal.

        .. describe:: hash(x)

            Returns the post-like object's hash.

    Attributes
    ----------
    data: :class:`dict`
        The raw data directly taken from the response generated by Weverse's API.
    id: :class:`int`
        The ID of the post-like object.
    like_id: Optional[:class:`str`]
        The ID of the like on the post-like object, if the user has liked the
        post-like object.
    body: :class:`str`
        The body that is displayed on the https://weverse.io website.
        Consider using :attribute:`plain_body` if you do not want markdowns
        and unnecessary information.
    plain_body: :class:`str`
        The plain body of the post-like object that does not have markdowns
        and unnecessary information.
    url: :class:`str`
        The URL that leads to the post-like object.
    extension: :class:`dict`
        Any additional information in the post will be stored in this
        attribute. However, most if not all posts of the
        :class:`sunverse.objects.Post` object do not have any data
        stored in this attribute. Only posts of other objects like
        :class:`sunverse.objects.Media` would have data stored in this
        attribute.
    bookmarked: :class:`bool`
        Whether the user has bookmarked the post-like object.
    locked: :class:`bool`
        UNDETERMINED FUNCTIONALITY: (Has always returned `False`.)
    has_product: :class:`bool`
        UNDETERMINED FUNCTIONALITY: (Has always returned `False`.)
    hide_from_artist: :class:`bool`
        Whether the post-like object is hidden from artists. Will most likely
        ever return `True` in posts posted by non-artists.
    membership_only: :class:`bool`
        Whether the post-like object can only be seen by users who have a
        paid membership in the community the post-like object belongs to.
    like_count: :class:`int`
        The number of likes for the post-like object.
    comment_count: :class:`int`
        The number of comments for the post-like object.
    published_at: :class:`int`
        The time the post-like object got created at, in epoch.
    post_type: :class:`str`
        The post type of the post-like object.
    section_type: :class:`str`
        The section the post-type object falls under.
    tags: list[:class:`str`]
        The list of hashtags used in the post-like object.
    community_id: :class:`int`
        The community ID of the community the post-like object belongs to.
    author_id: :class:`int`
        The author ID of the author who wrote the post-like object.
    """

    __slots__ = (
        "data",
        "id",
        "like_id",
        "body",
        "plain_body",
        "url",
        "extension",
        "bookmarked",
        "locked",
        "has_product",
        "hide_from_artist",
        "membership_only",
        "like_count",
        "comment_count",
        "published_at",
        "post_type",
        "section_type",
        "tags",
        "community_id",
        "author_id",
    )

    def __init__(self, data: dict):
        self.data: dict = data
        self.id: str = data["postId"]
        self.like_id: Optional[str] = data.get("viewerEmotionId")
        self.body: str = data["body"]
        self.plain_body: str = data["plainBody"]
        self.url: str = data["shareUrl"]
        self.extension: dict = data["extension"]
        self.bookmarked: bool = data["bookmarked"]
        self.locked: bool = data["locked"]
        self.has_product: bool = data["hasProduct"]
        self.hide_from_artist: bool = data["hideFromArtist"]
        self.membership_only: bool = data["membershipOnly"]
        self.like_count: int = data["emotionCount"]
        self.comment_count: int = data["commentCount"]
        self.published_at: int = data["publishedAt"]
        self.post_type: str = data["postType"]
        self.section_type: str = data["sectionType"]
        self.tags: list[str] = data["tags"]
        self.community_id: int = data["community"]["communityId"]
        self.author_id: str = data["author"]["memberId"]

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.id == other.id

        raise NotImplementedError

    def __hash__(self):
        return hash(self.id)


class Post(Postlike):
    """Represents a Weverse Post. Inherits from :class:`sunverse.objects.Postlike`.

    Shares the same attributes with :class:`sunverse.objects.Postlike`.

    .. container:: operations

        .. describe:: str(x)

            Returns the post's plain body.
    """

    def __repr__(self):
        return f"Post post_id={self.id}, plain_body={self.plain_body}"

    def __str__(self):
        return self.plain_body

    @property
    def photos(self) -> list[Photo]:
        """list[:class:`sunverse.objects.Photo`]: A list of
        :class:`sunverse.objects.Photo` objects in the post.

        Returns an empty list if there are no photos.
        """
        if not self.data["attachment"].get("photo"):
            return []

        return [
            Photo(photo_data)
            for photo_data in self.data["attachment"]["photo"].values()
        ]

    @property
    def videos(self) -> list[Video]:
        """list[:class:`sunverse.objects.Video`]: A list of
        :class:`sunverse.objects.Video` objects in the post.

        Returns an empty list if there are no videos.
        """
        if not self.data["attachment"].get("video"):
            return []

        return [
            Video(video_data)
            for video_data in self.data["attachment"]["video"].values()
        ]

    @property
    def snippets(self) -> list[Snippet]:
        """list[:class:`sunverse.objects.Snippet`]: A list of
        :class:`sunverse.objects.Snippet` objects in the post.

        Returns an empty list if there are no photos.
        """
        if not self.data["attachment"].get("snippet"):
            return []

        return [
            Snippet(snippet_data)
            for snippet_data in self.data["attachment"]["snippet"].values()
        ]
